﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;

namespace SynoManager.DownloadStation.Plugins
{
    public class ClipboardWatcher : Control
    {
        private const int WM_DRAWCLIPBOARD = 0x0308;
        private const int WM_CHANGECBCHAIN = 0x030D;
        /// <summary>
        /// Register this form as a Clipboard Viewer application
        /// </summary>
        public void RegisterClipboardViewer()
        {
            nextViewerInChain = User32.SetClipboardViewer(this.Handle);
        }

        /// <summary>
        /// Remove this form from the Clipboard Viewer list
        /// </summary>
        public void UnregisterClipboardViewer()
        {
            User32.ChangeClipboardChain(this.Handle, nextViewerInChain);
        }

        IntPtr nextViewerInChain;

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                //
                // The WM_DRAWCLIPBOARD message is sent to the first window 
                // in the clipboard viewer chain when the content of the 
                // clipboard changes. This enables a clipboard viewer 
                // window to display the new content of the clipboard. 
                //
                case WM_DRAWCLIPBOARD:

                    Debug.WriteLine("WindowProc DRAWCLIPBOARD: " + m.Msg, "WndProc");

                    GetClipboardData();

                    //
                    // Each window that receives the WM_DRAWCLIPBOARD message 
                    // must call the SendMessage function to pass the message 
                    // on to the next window in the clipboard viewer chain.
                    //
                    User32.SendMessage(nextViewerInChain, m.Msg, m.WParam, m.LParam);
                    break;


                //
                // The WM_CHANGECBCHAIN message is sent to the first window 
                // in the clipboard viewer chain when a window is being 
                // removed from the chain. 
                //
                case WM_CHANGECBCHAIN:
                    Debug.WriteLine("WM_CHANGECBCHAIN: lParam: " + m.LParam, "WndProc");

                    // When a clipboard viewer window receives the WM_CHANGECBCHAIN message, 
                    // it should call the SendMessage function to pass the message to the 
                    // next window in the chain, unless the next window is the window 
                    // being removed. In this case, the clipboard viewer should save 
                    // the handle specified by the lParam parameter as the next window in the chain. 

                    //
                    // wParam is the Handle to the window being removed from 
                    // the clipboard viewer chain 
                    // lParam is the Handle to the next window in the chain 
                    // following the window being removed. 
                    if (m.WParam == nextViewerInChain)
                    {
                        //
                        // If wParam is the next clipboard viewer then it
                        // is being removed so update pointer to the next
                        // window in the clipboard chain
                        //
                        nextViewerInChain = m.LParam;
                    }
                    else
                    {
                        User32.SendMessage(nextViewerInChain, m.Msg, m.WParam, m.LParam);
                    }
                    break;

                default:
                    //
                    // Let the form process the messages that we are
                    // not interested in
                    //
                    base.WndProc(ref m);
                    break;

            }

        }

        public event ClipboardEventHandler NewData;
        private void GetClipboardData()
        {
            // check if indeed contains text
            if (NewData != null)
                NewData(this, new ClipboardEventArgs(Clipboard.GetText()));
        }

        /// <summary>
        /// Windows User32 DLL declarations
        /// </summary>
        public class User32
        {
            [DllImport("User32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr SetClipboardViewer(IntPtr hWnd);

            [DllImport("User32.dll", CharSet = CharSet.Auto)]
            public static extern bool ChangeClipboardChain(
                IntPtr hWndRemove,  // handle to window to remove
                IntPtr hWndNewNext  // handle to next window
                );

            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
        }
    }

    public delegate void ClipboardEventHandler(object sender, ClipboardEventArgs args);
    public class ClipboardEventArgs : EventArgs
    {
        private readonly string text;

        public ClipboardEventArgs(string text)
        {
            this.text = text;
        }

        public string Text
        {
            get { return text; }
        }
    }
}
